package com.xuzimian.globaldemo.spring.springmvc.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.concurrent.FailureCallback;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.SuccessCallback;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.WebAsyncTask;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.Callable;

/**
 * Servlet3.0 规范新增了对异步请求的支持，Spring MVC也在此基础上对异步请求提供了方便。异步请求是在处理比较
 * 耗时的业务时先将request返回，然后另起线程处理耗时业务，处理完成后再返回给用户。
 * <p>
 * 异步请求的核心原理主要分两大类：一类是轮询，一类是长连接。
 * 轮询就是定时自动发起请求检查有没有需要返回的数据;长连接的原理是在客户端发起请求，服务端处理并返回后并不结束
 * 连接，这样就可以在后面再次返回给客户端数据。
 * Servlet对异步请求的支持是采用的长连接方式，也就是说在异步请求中原始的请求返回的时候并没有关闭连接，关闭的只是
 * 处理请求的那个线程，只有在异步请求全部处理完之后才会关闭连接。
 * <p>
 * Servlet 3.0 处理异步需要三步：
 * 1.配置Servlet时将async-suppported设置为true。
 * 2.在Servlet处理方法中调用Request的startAsync方法启动异步处理。
 * 3.使用第二步中返回的AsyncContext处理异步请求。
 * <p>
 * ---------------------------------------------------------------------------------------------------------------
 * <p>
 * Spring Mvc 为了方便使用异步请求专门提供了AsyncWebRequest类型的request,并且提供了处理异步请求的管理器WebAsyncMannager
 * 和工具WebAsyncUtils。
 * SpringMvc将异步请求细分为了Callable,WebAsyncTask,DeferredResult和ListenableFuture四种类型。
 * ListenableFuture 在Java的Future基础上增加了设置回调方法的功能，主要用于需要在处理器中调用别的资源的情况，SpringMvc专门
 * 提供了AsyncRestTemplate方法调用别的资源，并返回 ListenableFuture 类型。
 *
 * @program: global-demo
 * @description: 异步处理控制器Demo
 * @author: xzm
 * @create: 2019-04-25 09:39
 **/
@Controller
@RequestMapping("/asyn")
public class AsynController {

    @RequestMapping("/testCallable")
    @ResponseBody
    public Callable<String> testCallable() {
        System.out.println("Controller开始执行！");
        Callable<String> callable = () -> {
            Thread.sleep(5000);

            System.out.println("实际工作执行完成！");

            return "succeed!";
        };
        System.out.println("Controller执行结束！");
        return callable;
    }

    @RequestMapping("/testWebAsyncTask")
    @ResponseBody
    public WebAsyncTask<String> testWebAsyncTask() {
        System.out.println("Controller开始执行！");
        WebAsyncTask<String> webAsyncTask = new WebAsyncTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(5000);
                System.out.println("实际工作执行完成！");
                return "succeed!";
            }
        });
        System.out.println("Controller执行结束！");
        return webAsyncTask;
    }

    /**
     * ListenableFuture 在Future的基础上增加了处理成功和处理失败的回调方法。Future只能调用
     * get方法来主动拉取数据，而且get方法还是阻塞的，而ListenableFuture可以等待处理完成后
     * 自己将结果推过来，而且不会阻塞线程。
     *
     * @return
     */
    @RequestMapping("/testListenableFuture")
    @ResponseBody
    public ListenableFuture<ResponseEntity<String>> testListenableFuture() {
        System.out.println("Controller开始执行！");
        ListenableFuture<ResponseEntity<String>> listenableFuture = new AsyncRestTemplate()
                .getForEntity("http://localhost:8080/home", String.class);

        listenableFuture.addCallback(new SuccessCallback<ResponseEntity<String>>() {
            @Override
            public void onSuccess(ResponseEntity<String> result) {
                System.out.println("testListenableFuture 执行成功！");
            }
        }, new FailureCallback() {
            @Override
            public void onFailure(Throwable ex) {
                System.out.println("testListenableFuture 执行失败！");
            }
        });

        System.out.println("Controller执行结束！");
        return listenableFuture;
    }

    @RequestMapping("/testDeferredResult")
    public DeferredResult<String> testDeferredResult() {
        DeferredResult<String> deferredResult = new DeferredResult<>();
        deferredResult.setResult("添加点数据！");
        return deferredResult;
    }

    @RequestMapping("/stream")
    public ResponseBodyEmitter testResponseBodyEmitter() {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter();
        Runnable task = () -> {
            try {
                emitter.send("Hello");
                emitter.send("Bye");
                emitter.complete();
            } catch (IOException e) {
                e.printStackTrace();
            }
        };
        try {
            emitter.send("你好！");
        } catch (IOException e) {
            e.printStackTrace();
        }
        new Thread(task).start();
        return emitter;
    }

    /**
     * 让方法返回StreamingResponseBody，Spring会将二进制流直接返回给客户端
     *
     * @return
     */
    @RequestMapping("/streamBody")
    public StreamingResponseBody testStreamingResponseBody() {
        return new StreamingResponseBody() {
            @Override
            public void writeTo(OutputStream outputStream) throws IOException {
                outputStream.write("123456".getBytes());
            }
        };
    }

}
